package main;

import java.util.Collections;
import java.util.List;

import utils.HeuristicsCarrierUtils;
import utils.IterationalCarrierUtils;
import data.Carrier;
import data.CarrierType;
import data.Package;

public class CalculationNode implements Runnable {

	int id = 0;
	List<List<CarrierType>> carrierTypes;
	List<List<Package>> packages;
	List<List<Carrier>> carriers;

	float[] bestCarriersFieldBaseSum;
	float[] bestCarriersMinCapacitySum;

	int numberOfIterations, packageMixerIterations,
			capacityMinimalizationIterations;

	long timeStart;

	/**
	 * @param timeStart
	 * @param carrierTypes
	 * @param packages
	 * @param initialSeed
	 */
	public CalculationNode(int id, long timeStart,
			List<List<CarrierType>> carrierTypes, List<List<Package>> packages,
			List<List<Carrier>> carriers, float[] bestCarriersFieldBaseSum,
			float[] bestCarriersMinCapacitySum) {
		this.timeStart = timeStart;
		this.id = id;
		this.carrierTypes = carrierTypes;
		this.packages = packages;
		this.carriers = carriers;
		this.bestCarriersFieldBaseSum = bestCarriersFieldBaseSum;
		this.bestCarriersMinCapacitySum = bestCarriersMinCapacitySum;
	}

	@Override
	public void run() {
		IterationalCarrierUtils iterationalCarrierUtils = new IterationalCarrierUtils();
		HeuristicsCarrierUtils heuristicCarrierUtils = new HeuristicsCarrierUtils();
		// losowe szukanie rozwiazania
		while (System.currentTimeMillis() - 400 < timeStart) {
			randomPermutations(iterationalCarrierUtils);
		}
		// proba zamiany paczek
		while (System.currentTimeMillis() - 900 < timeStart) {
			packageMixer(heuristicCarrierUtils);
		}
		// minimalizacja najmniejszego carriera
		while (System.currentTimeMillis() - 950 < timeStart) {
			capacityMinimalization(heuristicCarrierUtils);
		}
	}

	private void packageMixer(HeuristicsCarrierUtils heuristicCarrierUtils) {
		for (int i = 0; i < carriers.size(); i++) {
			this.compareResultsAndKeepBest(heuristicCarrierUtils
					.mixPackages(carrierTypes.get(i),
							carriers.get(i)), i);
		}
		packageMixerIterations++;
	}

	private void capacityMinimalization(
			HeuristicsCarrierUtils heuristicCarrierUtils) {
		for (int i = 0; i < carriers.size(); i++) {
			this.compareResultsAndKeepBest(heuristicCarrierUtils
					.minimizeMinCapacity(carrierTypes.get(i),
							carriers.get(i)), i);
		}
		capacityMinimalizationIterations++;
	}

	private void randomPermutations(
			IterationalCarrierUtils iterationalCarrierUtils) {
		for (int i = 0; i < packages.size(); i++) {
			Collections.shuffle(packages.get(i));
			this.compareResultsAndKeepBest(
					iterationalCarrierUtils.pushPackagesOnCarriers(
							packages.get(i), carrierTypes.get(i)), i);
		}
		numberOfIterations++;
	}

	public int getIterIterations() {
		return this.numberOfIterations;
	}

	public int getCapacityMinimalizationIterations() {
		return this.capacityMinimalizationIterations;
	}

	public int getPackageMixerIterations() {
		return packageMixerIterations;
	}

	/**
	 * @return the carriers
	 */
	public List<List<Carrier>> getCarriers() {
		return carriers;
	}

	/**
	 * Póki sumuje pola podstawy i biore minimum. Jesli jakies sa rowne to
	 * dopiero patrze na min objetosc
	 * 
	 * @param list
	 * @param id
	 */
	private synchronized void compareResultsAndKeepBest(List<Carrier> list,
			int id) {
		float currentCarrierFieldBaseSum = 0;
		float currentCarrierMinCapacitySum = 0;

		for (Carrier carrier : list) {
			currentCarrierFieldBaseSum += carrier.getType().getFieldBase();
			currentCarrierMinCapacitySum += carrier.getCapacity();
		}
		if (currentCarrierFieldBaseSum <= bestCarriersFieldBaseSum[id]) {
			if (currentCarrierFieldBaseSum == bestCarriersFieldBaseSum[id]) {
				if (currentCarrierMinCapacitySum < bestCarriersMinCapacitySum[id]) {
					saveBestConfiguration(list, id, currentCarrierFieldBaseSum,
							currentCarrierMinCapacitySum);
				}
			} else {
				saveBestConfiguration(list, id, currentCarrierFieldBaseSum,
						currentCarrierMinCapacitySum);
			}
		}
	}

	/**
	 * @param list
	 * @param id
	 * @param currentCarrierFieldBaseSum
	 * @param currentCarrierMinCapacitySum
	 */
	private synchronized void saveBestConfiguration(List<Carrier> list, int id,
			float currentCarrierFieldBaseSum, float currentCarrierMinCapacitySum) {
		carriers.set(id, list);
		bestCarriersFieldBaseSum[id] = currentCarrierFieldBaseSum;
		bestCarriersMinCapacitySum[id] = currentCarrierMinCapacitySum;
	}
}
